import PROTOCOL_CONFIG from "../config/protocolConfig";

class TouchController {
    constructor(options) {
        this.options = options;
    }

    setResolution(resolution) {
        this.options.resolution = resolution;
    }

    init() {
        this._initPlayer();
        this._initMouseEventListener();
        this._initTouchEventListener();
        this._initWindowsEventListener();
    }

    destroy() {

    }

    _initPlayer() {
        let resolution = this.options.resolution;

        const documentW = document.documentElement.clientWidth;
        const documentH = document.documentElement.clientHeight;

        const resolutionRatio = resolution.x / resolution.y;
        const documentRatio = documentW / documentH;

        let w;
        let h;
        if (resolutionRatio > documentRatio) {
            w = documentW;
            h = w * resolution.y / resolution.x;
        } else {
            h = documentH;
            w = h * resolution.x / resolution.y;
        }

        this.options.player.style.width = w + 'px';
        this.options.player.style.height = h + 'px';
        this.options.player.style.marginTop = (documentH - h) / 2 + 'px';

        this.ratio = resolution.x / w;
    }

    _initMouseEventListener() {
        this.options.player.addEventListener('mousedown', this._onMouseDown.bind(this));
        this.options.player.addEventListener('mousemove', this._onMouseMove.bind(this));
        this.options.player.addEventListener('mouseup', this._onMouseUp.bind(this));
    }

    _initTouchEventListener() {
        this.options.player.addEventListener('touchstart', this._onTouchStart.bind(this));
        this.options.player.addEventListener('touchmove', this._onTouchMove.bind(this));
        this.options.player.addEventListener('touchend', this._onTouchEnd.bind(this));
        this.options.player.addEventListener('touchcancel', this._onTouchCancel.bind(this));
    }

    _initWindowsEventListener() {
        window.addEventListener('resize', this._onResize.bind(this));
    }

    _onMouseDown(event) {
        this._preventDefault(event);
        this.isMouseDown = true;
        this._sendTouchEvent({
            touchId: 0,
            touchEvent: event,
            isTouchPointer: false,
            touchFunc: '_sendTouchStart'
        });
    }

    _onMouseMove(event) {
        this._preventDefault(event);
        if (!this.isMouseDown) {
            return;
        }
        this._sendTouchEvent({
            touchId: 0,
            touchEvent: event,
            isTouchPointer: false,
            touchFunc: '_sendTouchMove'
        });
    }

    _onMouseUp(event) {
        this._preventDefault(event);
        this.isMouseDown = false;
        this._sendTouchEvent({
            touchId: 0,
            touchEvent: event,
            isTouchPointer: false,
            touchFunc: '_sendTouchEnd'
        });
    }

    _onTouchStart(event) {
        this._preventDefault(event);
        const touchList = event['changedTouches'] || {length: 0};
        for (let len = touchList.length, index = 0; index < len; index++) {
            const touch = touchList.item(index);
            const touchId = this._getTouchId(touch);
            this._sendTouchEvent({
                touchId: touchId,
                touchEvent: touch,
                isTouchPointer: this._isMultiPointer(),
                touchFunc: '_sendTouchStart',
                touchPointerFunc: '_sendTouchPointerStart'
            });
        }
    }

    _onTouchMove(event) {
        this._preventDefault(event);
        const touchList = event['changedTouches'] || {length: 0};
        for (let len = touchList.length, index = 0; index < len; index++) {
            const touch = touchList.item(index);
            const touchId = this._getTouchId(touch);
            this._sendTouchEvent({
                touchId: touchId,
                touchEvent: touch,
                isTouchPointer: this._isMultiPointer(),
                touchFunc: '_sendTouchMove',
                touchPointerFunc: '_sendTouchMove'
            });
        }
    }

    _onTouchEnd(event) {
        this._preventDefault(event);
        const touchList = event['changedTouches'] || {length: 0};
        for (let len = touchList.length, index = 0; index < len; index++) {
            const touch = touchList.item(index);
            const touchId = this._getTouchId(touch);
            this._sendTouchEvent({
                touchId: touchId,
                touchEvent: touch,
                isTouchPointer: this._isMultiPointer(),
                touchFunc: '_sendTouchEnd',
                touchPointerFunc: '_sendTouchPointerEnd'
            });
            this._freeTouchId(touchId);
            this.touchIdMap.delete(touch.identifier);
        }
    }

    _onTouchCancel(event) {
        this._onTouchEnd(event);
    }

    _onResize() {
        this._initPlayer();
    }

    _preventDefault(event) {
        event.stopPropagation();
        event.preventDefault();
    }

    _getEventPos(event) {
        const documentRect = document.documentElement.getClientRects().item(0);
        const displayRect = this.options.player.getClientRects().item(0);
        return {
            x: Math.max(event.pageX - displayRect.left, 0),
            y: Math.max(event.pageY - displayRect.top + documentRect.top, 0),
        }
    }

    _sendTouchEvent(touchInfo) {
        const eventPos = this._getEventPos(touchInfo.touchEvent);
        if (touchInfo.isTouchPointer && touchInfo.touchPointerFunc) {
            this[touchInfo.touchPointerFunc](touchInfo.touchId, {
                x: eventPos.x * this.ratio,
                y: eventPos.y * this.ratio
            });
            return;
        }
        this[touchInfo.touchFunc](touchInfo.touchId, {
            x: eventPos.x * this.ratio,
            y: eventPos.y * this.ratio
        });
    }

    _sendTouchStart(touchId, touchPos) {
        this.options.messageHandler.sendTouchMsgData({
            action: PROTOCOL_CONFIG.ACTIONS_TYPE.DOWN,
            ...this._generateMouseCommonData(touchId, touchPos)
        });
    }

    _sendTouchPointerStart(touchId, touchPos) {
        this.options.messageHandler.sendTouchMsgData({
            action: PROTOCOL_CONFIG.ACTIONS_TYPE.POINTER_DOWN,
            ...this._generateMouseCommonData(touchId, touchPos)
        });
    }

    _sendTouchMove(touchId, touchPos) {
        this.options.messageHandler.sendTouchMsgData({
            action: PROTOCOL_CONFIG.ACTIONS_TYPE.MOVE,
            ...this._generateMouseCommonData(touchId, touchPos)
        });
    }

    _sendTouchPointerEnd(touchId, touchPos) {
        this.options.messageHandler.sendTouchMsgData({
            action: PROTOCOL_CONFIG.ACTIONS_TYPE.POINTER_UP,
            ...this._generateMouseCommonData(touchId, touchPos)
        });
    }

    _sendTouchEnd(touchId, touchPos) {
        this.options.messageHandler.sendTouchMsgData({
            action: PROTOCOL_CONFIG.ACTIONS_TYPE.UP,
            ...this._generateMouseCommonData(touchId, touchPos)
        });
    }

    _generateMouseCommonData(touchId, touchPos) {
        const boundingClientRect = this.options.player.getBoundingClientRect();
        const w = boundingClientRect.width * this.ratio;
        const h = boundingClientRect.height * this.ratio;
        return {
            id: touchId,
            x: touchPos.x,
            y: touchPos.y,
            pressure: 129,
            time: -1,
            orientation: 0,
            height: h,
            width: w
        }
    }

    _getTouchId(touchEvent) {
        if (!this.touchIdMap) {
            this.touchIdMap = new Map();
        }
        let touchIdMap = this.touchIdMap;
        let touchId = touchIdMap.get(touchEvent.identifier);
        if (touchId === null || touchId === undefined) {
            touchId = this._allocateTouchId();
        }
        touchIdMap.set(touchEvent.identifier, touchId);
        return touchId;
    }

    _allocateTouchId() {
        if (!this.touchIds) {
            this.touchIds = new Uint8Array(20);
        }
        let touchIds = this.touchIds;
        const index = touchIds.indexOf(0);
        if (index === -1) {
            touchIds.map(() => 0);
            touchIds[0] = 1;
            return 0;
        }
        touchIds[index] = 1;
        return index;
    }

    _freeTouchId(touchId) {
        this.touchIds[touchId] = 0;
    }

    _isMultiPointer() {
        return this.touchIdMap.size > 1;
    }
}

export default TouchController;